
package pl.wroc.uni.ii.evolution.engine.operators.spacespecific.binaryvector;

import pl.wroc.uni.ii.evolution.engine.EvPopulation;
import pl.wroc.uni.ii.evolution.engine.individuals.EvBinaryVectorIndividual;
import pl.wroc.uni.ii.evolution.engine.operators.spacespecific.binaryvector.comitstructs.EvGraph;
import pl.wroc.uni.ii.evolution.engine.operators.spacespecific.binaryvector.comitstructs.EvTree;
import pl.wroc.uni.ii.evolution.engine.prototype.EvOperator;

/**
 * Class implementing Comit operator for population of BinaryIndividual.<BR>
 * <BR>
 * Operator builds new population from probability tree made of Population given
 * in apply method in following steps:<BR> - create matrix of similarity of
 * individuals<BR> - create graph from matrix with weights containig values
 * from matrix<BR> - create maximum spinning tree from graph<BR> - randomly
 * select node in tree spinning tree<BR> - create probability tree with root as
 * selected node and add probability values in every node comparing individuals
 * representing connected nodes.<BR>
 * 
 * @author Kacper Gorski, Olgierd Humenczuk<BR>
 * 
 * 
 */
public class EvBinaryVectorCOMITOperator implements EvOperator<EvBinaryVectorIndividual> {

  /**
   * size of new population
   */
  private int new_population_size = 0;

  /**
   * Constructor that can set size of the new population
   * 
   * @param new_population_size
   */
  public EvBinaryVectorCOMITOperator( int new_population_size ) {
    this.new_population_size = new_population_size;
  }

  /**
   * Run this operator on given population and return a new - better one.
   * 
   * @param population -
   *        input population.
   * @return new better population generated from the tree of probability
   *         created from given population, new population size is given as
   *         constructor param.
   */
  @SuppressWarnings("unchecked")
  public EvPopulation<EvBinaryVectorIndividual> apply( EvPopulation<EvBinaryVectorIndividual> population ) {

    if ( new_population_size <= 0 ) {
      throw new IllegalArgumentException( "Size of new population should be greater then zero" );
    }

    int ind_size = population.get( 0 ).getDimension();

    // creating matrix of similarity from individual characteristics
    int[][] matrix = new int[ind_size][ind_size];
    for( int i = 0; i < ind_size; i++ ) {
      for( int j = 0; j < i; j++ ) {
        matrix[i][j] = getNoSameBits( population, i, j );
      }
    }

    // creating graph from matrix
    EvGraph graph = new EvGraph( matrix );

    // creating maximum spanning tree from graph
    EvTree tree = graph.getMaximumSpanningTree();
    
    // building tree of probability from population
    tree.setProbability( population );

    // generating new population       
    EvBinaryVectorIndividual[] ind_table = new EvBinaryVectorIndividual[new_population_size];
    for (int i=0; i < new_population_size; i++) {
      ind_table[i] = new EvBinaryVectorIndividual( ind_size );
    }
    tree.randomizeIndividual( ind_table );
    EvPopulation<EvBinaryVectorIndividual> new_population = new EvPopulation<EvBinaryVectorIndividual>(
        ind_table );    
    
    new_population.setObjectiveFunction( population.get( 0 ).getObjectiveFunction() );
    return new_population;
  }

  /**
   * Little help function that count number of same bits at specified location 
   * in population matrix
   * 
   * @param population -
   *        population object
   * @param compare_bit -
   *        the row to be compared
   * @param compare_to_bit -
   *        the row to be compared to
   * @return number of same bits in that two rows
   */
  private int getNoSameBits( EvPopulation<EvBinaryVectorIndividual> population, int compare_bit,
      int compare_to_bit ) {
    int col_size = population.size();
    int same_bits = 0;

    EvBinaryVectorIndividual[] individual_table = population.toArray(new EvBinaryVectorIndividual[population.size()]);
    
    // for every column in matrix get desired bit in desire row and compare it
    // with
    // another bit in another row in the same individual
    for( int it = 0; it < col_size; it++ ) {
      if ( individual_table[it].vector[compare_bit] == individual_table[it].vector[compare_to_bit] ) {
        same_bits++;
      }
    }

    return same_bits;
  }

}
